home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Newswatcher 2.0b22 / NW Source / Source / listutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-20  |  24.9 KB  |  979 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     listutil.c
  4.  
  5.     This reusable module contains miscellaneous List Manager
  6.     utility routines.
  7.     
  8.     Copyright © 1994, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <stdio.h>
  13.  
  14. #include "def.h"
  15. #include "listutil.h"
  16. #include "memutil.h"
  17. #include "drawutil.h"
  18.  
  19.  
  20.  
  21. /*----------------------------------------------------------------------------
  22.     InvalCell 
  23.     
  24.     Invalidate a cell.
  25.             
  26.     Entry:    theCell = the cell.
  27.             theList = list handle.
  28. ----------------------------------------------------------------------------*/
  29.  
  30. static void InvalCell (Cell theCell, ListHandle theList)
  31. {
  32.     Rect visible, inval;
  33.     short rViewTop, cellHeight;
  34.     
  35.     inval = (**theList).rView;
  36.     rViewTop = inval.top;
  37.     visible = (**theList).visible;
  38.     cellHeight = (**theList).cellSize.v;
  39.     inval.top = rViewTop + (theCell.v - visible.top) * cellHeight;
  40.     inval.bottom = inval.top + cellHeight;
  41.     InvalRect(&inval);
  42. }
  43.  
  44.  
  45.  
  46. /*----------------------------------------------------------------------------
  47.     SelectCellRange 
  48.     
  49.     Quickly select or deselect all the cells in a range.
  50.             
  51.     Entry:    select = true to select all, false to deselect all.
  52.             first = first row to select.
  53.             last = last row to select.
  54.             theList = list handle.
  55. ----------------------------------------------------------------------------*/
  56.  
  57. static void SelectCellRange (Boolean select, short first, short last, 
  58.     ListHandle theList)
  59. {
  60.     short *p, *pEnd, visTop, visBot;
  61.     Rect rView;
  62.     Cell theCell;
  63.     Boolean cellChanged;
  64.     Point cellSize;
  65.     GrafPtr port;
  66.     char state;
  67.     
  68.     GetPort(&port);
  69.     SetPort((**theList).port);
  70.     rView = (**theList).rView;
  71.     cellSize = (**theList).cellSize; 
  72.     visTop = (**theList).visible.top;
  73.     visBot = (**theList).visible.bottom;
  74.     SetPt(&theCell, 0, first);
  75.     state = MyHGetState(theList);
  76.     MyHLock(theList);
  77.     p = (**theList).cellArray + first;
  78.     pEnd = p + last - first + 1;
  79.     while (p < pEnd) {
  80.         if (select) {
  81.             cellChanged = (*p & 0x8000) == 0;
  82.             *p |= 0x8000;
  83.         } else {
  84.             cellChanged = (*p & 0x8000) != 0;
  85.             *p &= 0x7fff;
  86.         }
  87.         if (cellChanged && theCell.v >= visTop && theCell.v <= visBot) 
  88.             InvalCell(theCell, theList);
  89.         p++;
  90.         theCell.v++;
  91.     }
  92.     MyHSetState(theList, state);
  93.     SetPort(port);
  94. }
  95.  
  96.  
  97.  
  98. /*----------------------------------------------------------------------------
  99.     SelectOrDeselectAllListItems 
  100.     
  101.     Quickly select or deselect all the items in a list.
  102.             
  103.     Entry:    theList = list handle.
  104.             select = true to select all, false to deselect all.
  105. ----------------------------------------------------------------------------*/
  106.  
  107. void SelectOrDeselectAllListItems (ListHandle theList, Boolean select)
  108. {
  109.     SelectCellRange(select, 0, (**theList).dataBounds.bottom - 1, theList);
  110. }
  111.  
  112.  
  113.  
  114. /*----------------------------------------------------------------------------
  115.     SelectSingleListItem 
  116.     
  117.     Quickly select a single item in a list and deselect all the others.
  118.             
  119.     Entry:    theList = list handle.
  120.             theCell = item to be selected.
  121. ----------------------------------------------------------------------------*/
  122.  
  123. void SelectSingleListItem (ListHandle theList, Cell theCell)
  124. {
  125.     short *p, *pEnd, visTop, visBot;
  126.     Rect rView;
  127.     Cell tmpCell;
  128.     Boolean cellChanged, setIt;
  129.     Point cellSize;
  130.     GrafPtr port;
  131.     char state;
  132.     
  133.     GetPort(&port);
  134.     SetPort((**theList).port);
  135.     rView = (**theList).rView;
  136.     cellSize = (**theList).cellSize; 
  137.     visTop = (**theList).visible.top;
  138.     visBot = (**theList).visible.bottom;
  139.     SetPt(&tmpCell, 0, 0);
  140.     state = MyHGetState(theList);
  141.     MyHLock(theList);
  142.     p = (**theList).cellArray;
  143.     pEnd = p + (**theList).dataBounds.bottom;
  144.     
  145.     if ((**theList).lActive && ((WindowPeek)(**theList).port)->hilited) {
  146.     
  147.         while (p < pEnd) {
  148.             if (tmpCell.v == theCell.v) {
  149.                 cellChanged = (*p & 0x8000) == 0;
  150.                 setIt = true;
  151.             } else {
  152.                 cellChanged = (*p & 0x8000) != 0;
  153.                 setIt = false;
  154.             }
  155.             if (cellChanged) {
  156.                 if (tmpCell.v >= visTop && tmpCell.v < visBot) {
  157.                     LSetSelect(setIt, tmpCell, theList);
  158.                 } else if (setIt) {
  159.                     *p |= 0x8000;
  160.                 } else {
  161.                     *p &= 0x7fff;
  162.                 }
  163.             }
  164.             p++;
  165.             tmpCell.v++;
  166.         }
  167.     
  168.     } else {
  169.         
  170.         while (p < pEnd) {
  171.             if (tmpCell.v == theCell.v) {
  172.                 cellChanged = (*p & 0x8000) == 0;
  173.                 *p |= 0x8000;
  174.             } else {
  175.                 cellChanged = (*p & 0x8000) != 0;
  176.                 *p &= 0x7fff;
  177.             }
  178.             if (cellChanged && tmpCell.v >= visTop && tmpCell.v <= visBot)
  179.                 InvalCell(tmpCell, theList);
  180.             p++;
  181.             tmpCell.v++;
  182.         }
  183.     
  184.     }
  185.     
  186.     MyHSetState(theList, state);
  187.     SetPort(port);
  188. }
  189.  
  190.  
  191.  
  192. /*----------------------------------------------------------------------------
  193.     ListHasSelectedCell 
  194.     
  195.     Check to see if a list has at least one selected cell.
  196.             
  197.     Entry:    theList = list handle.
  198.     
  199.     Exit:    function result = true if list has at least one selected cell.
  200. ----------------------------------------------------------------------------*/
  201.  
  202. Boolean ListHasSelectedCell (ListHandle theList)
  203. {
  204.     Cell theCell;
  205.     
  206.     SetPt(&theCell, 0, 0);
  207.     return LGetSelect(true, &theCell, theList);
  208. }
  209.  
  210.  
  211.  
  212. /*----------------------------------------------------------------------------
  213.     NumListItemsSelected 
  214.     
  215.     Count the number of selected items in a list.
  216.             
  217.     Entry:    theList = list handle.
  218.     
  219.     Exit:    function result = number of items selected.
  220. ----------------------------------------------------------------------------*/
  221.  
  222. short NumListItemsSelected (ListHandle theList)
  223. {
  224.     short num = 0, *p, *pEnd;
  225.  
  226.     p = (**theList).cellArray;
  227.     pEnd = p + (**theList).dataBounds.bottom;
  228.     while (p < pEnd) {
  229.         if (*p < 0) num++;
  230.         p++;
  231.     }
  232.     return num;
  233. }
  234.  
  235.  
  236.  
  237. /*----------------------------------------------------------------------------
  238.     MyLScroll
  239.     
  240.     Scroll a list a specified number of rows.
  241.             
  242.     Entry:    deltaRows = number of rows to scroll down (>0) or up (<0)
  243.             theList = list handle.
  244.     
  245.     This function avoids an error in the List Manager LScroll function
  246.     which causes crashes on some scrolls over 32K pixels. It only works on 
  247.     lists with 1 column.
  248. ----------------------------------------------------------------------------*/
  249.  
  250. void MyLScroll (short deltaRows, ListHandle theList)
  251. {
  252.     short numCellsToScroll;
  253.     long numPixelsToScroll;
  254.     GrafPtr port;
  255.  
  256.     GetPort(&port);
  257.     SetPort((**theList).port);
  258.     numCellsToScroll = deltaRows;
  259.     if (numCellsToScroll < 0) numCellsToScroll = -numCellsToScroll;
  260.     numPixelsToScroll = (long)numCellsToScroll * (long)(**theList).cellSize.v;
  261.     if (numPixelsToScroll > 0x3fff) {
  262.         LSetDrawingMode(false, theList);
  263.         LScroll(0, deltaRows, theList);
  264.         LSetDrawingMode(true, theList);
  265.         InvalRect(&(**theList).rView);
  266.     } else {
  267.         LScroll(0, deltaRows, theList);
  268.     }
  269.     SetPort(port);
  270. }
  271.  
  272.  
  273.  
  274. /*----------------------------------------------------------------------------
  275.     MyLScrollCellIntoView
  276.     
  277.     Scroll a cell into view.
  278.             
  279.     Entry:    theCell = cell.
  280.             theList = list handle.
  281.     
  282.     This function avoids an error in the List Manager LAutoScroll function
  283.     which causes crashes on some scrolls over 32K pixels. It only works on 
  284.     lists with 1 column.
  285. ----------------------------------------------------------------------------*/
  286.  
  287. void MyLScrollCellIntoView (Cell theCell, ListHandle theList)
  288. {
  289.     short top, bottom;
  290.     Rect visible;
  291.     
  292.     visible = (**theList).visible;
  293.     top = visible.top;
  294.     bottom = visible.bottom;
  295.     if (theCell.v < top) {
  296.         MyLScroll(theCell.v - top, theList);
  297.     } else if (theCell.v == bottom) {
  298.         if (NumListItemsSelected(theList) == 1) {
  299.             MyLScroll(bottom - top, theList);
  300.         } else {
  301.             MyLScroll(1, theList);
  302.         }
  303.     } else if (theCell.v > bottom) {
  304.         MyLScroll(theCell.v - bottom + 1, theList);
  305.     }
  306. }
  307.  
  308.  
  309.  
  310. /*----------------------------------------------------------------------------
  311.     MyLAutoScroll 
  312.     
  313.     Scroll the first selected cell into view.
  314.             
  315.     Entry:    theList = list handle.
  316.     
  317.     This function avoids an error in the List Manager LAutoScroll function
  318.     which causes crashes on some scrolls over 32K pixels. It only works on 
  319.     lists with 1 column.
  320. ----------------------------------------------------------------------------*/
  321.  
  322. void MyLAutoScroll (ListHandle theList)
  323. {
  324.     Cell theCell;
  325.     
  326.     SetPt(&theCell, 0, 0);
  327.     if (!LGetSelect(true, &theCell, theList)) return;
  328.     MyLScrollCellIntoView(theCell, theList);
  329. }
  330.  
  331.  
  332.  
  333. /*----------------------------------------------------------------------------
  334.     MyLScrollCenter
  335.     
  336.     Scroll a cell into the center of the visible part of the list, if
  337.     necessary.
  338.             
  339.     Entry:    theCell = cell.
  340.             theList = list handle.
  341. ----------------------------------------------------------------------------*/
  342.  
  343. void MyLScrollCenter (Cell theCell, ListHandle theList)
  344. {
  345.     short top, bottom, visHeight;
  346.     Rect visible;
  347.     
  348.     visible = (**theList).visible;
  349.     top = visible.top;
  350.     bottom = visible.bottom;
  351.     if (theCell.v >= top && theCell.v < bottom) return;
  352.     visHeight = bottom - top;
  353.     MyLScroll(theCell.v - top - (visHeight >>1), theList);
  354. }
  355.  
  356.  
  357.  
  358. /*----------------------------------------------------------------------------
  359.     BuildListSelectedCellsDragRegion
  360.     
  361.     Build a drag region for the selected cells in a list.
  362.             
  363.     Entry:    theList = handle to list.
  364.     
  365.     Exit:    *dragRgn = handle to drag region.
  366. ----------------------------------------------------------------------------*/
  367.  
  368. void BuildListSelectedCellsDragRegion (ListHandle theList, RgnHandle *dragRgn)
  369. {
  370.     RgnHandle rgn;
  371.     Cell curCell;
  372.     Rect cellRect;
  373.     Rect visible;
  374.     GrafPtr port;
  375.  
  376.     GetPort(&port);
  377.     SetPort((**theList).port);
  378.     visible = (**theList).visible;
  379.     rgn = NewRgn();
  380.     OpenRgn();
  381.         SetPt(&curCell, 0, visible.top);
  382.         while (LGetSelect(true, &curCell, theList) && curCell.v < visible.bottom) {
  383.             LRect(&cellRect, curCell, theList);
  384.             LocalToGlobalRect(&cellRect);
  385.             FrameRect(&cellRect);
  386.             curCell.v++;
  387.         }
  388.     CloseRgn(rgn);
  389.     SetPort(port);
  390.     OutlineRegion(rgn);
  391.     *dragRgn = rgn;
  392. }
  393.  
  394.  
  395.  
  396. /*----------------------------------------------------------------------------
  397.     DrawListDividingLIne
  398.     
  399.     Draw or erase a horizontal dividing line in a list.
  400.     
  401.     Entry:    theList = handle to the list.
  402.             destRow = row number of cell following the line to be
  403.                 drawn or erased.
  404. ----------------------------------------------------------------------------*/
  405.  
  406. void DrawListDividingLine (ListHandle theList, short destRow)
  407. {
  408.     GrafPtr port, listPort;
  409.     PenState savePen;
  410.     Rect clipRect;
  411.     static RgnHandle savedClip = nil;
  412.     short v;
  413.     
  414.     listPort = (**theList).port;
  415.  
  416.     GetPort(&port);
  417.     GetPenState(&savePen);
  418.     SetPort(listPort);
  419.     if (savedClip == nil) savedClip = NewRgn();
  420.     GetClip(savedClip);
  421.     
  422.     v = (**theList).cellSize.v * (destRow - (**theList).visible.top) +
  423.         (**theList).rView.top - 1;
  424.     
  425.     clipRect = (**theList).rView;
  426.     ClipRect(&clipRect);
  427.     PenMode(patXor);
  428.     PenPat(&qd.black);
  429.     PenSize(2, 2);
  430.     MoveTo((**theList).rView.left, v);
  431.     LineTo((**theList).rView.right - 2, v);
  432.     
  433.     SetClip(savedClip);
  434.     SetPenState(&savePen);
  435.     SetPort(port);
  436. }
  437.  
  438.  
  439.  
  440. /*----------------------------------------------------------------------------
  441.     ListDestinationRow
  442.     
  443.     Compute the destination row for a list given a location in
  444.     the list.
  445.     
  446.     Entry:    theList = handle to the list.
  447.             where = location in list in local coordinates.
  448.             
  449.     Exit:    function result = destination row number.
  450. ----------------------------------------------------------------------------*/
  451.  
  452. short ListDestinationRow (ListHandle theList, Point where)
  453. {
  454.     short cellHeight, destRow, numCells;
  455.     
  456.     cellHeight = (**theList).cellSize.v;
  457.     numCells = (**theList).dataBounds.bottom;
  458.     destRow = (where.v - (**theList).rView.top + (cellHeight >> 1)) / cellHeight +
  459.         (**theList).visible.top;
  460.     if (destRow < 0) destRow = 0;
  461.     if (destRow > numCells) destRow = numCells;
  462.     return destRow;
  463. }
  464.  
  465.  
  466.  
  467. /*----------------------------------------------------------------------------
  468.     MoveSelectedListCells
  469.     
  470.     Move the selected cells in a list to a new location.
  471.     
  472.     Entry:    theList = handle to list record
  473.             destRow = row number of cell following destination.
  474.             
  475.     Exit:    *changed = true if the cell order was changed.
  476.     
  477.     The cell data cannot exceed 256 bytes in length.
  478. ----------------------------------------------------------------------------*/
  479.  
  480. void MoveSelectedListCells (ListHandle theList, short destRow, Boolean *changed)
  481. {
  482.     short count;
  483.     Cell oldCell, newCell;
  484.     short dataLen, firstRow, prevRow, index;
  485.     Boolean contiguous = true;
  486.     Rect rView;
  487.     char cellData[256];
  488.     GrafPtr port;
  489.     
  490.     GetPort(&port);
  491.     SetPort((**theList).port);
  492.     
  493.     /* Count the number of cells to be moved. Also check to see if the
  494.        cells being moved are contiguous. */
  495.     
  496.     count = 0;
  497.     SetPt(&oldCell, 0, 0);
  498.     while (LGetSelect(true, &oldCell, theList)) {
  499.         if (count == 0) firstRow = oldCell.v;
  500.         if (contiguous && count > 0 && oldCell.v != prevRow + 1) contiguous = false;
  501.         prevRow = oldCell.v;
  502.         count++;
  503.         oldCell.v++;
  504.     }
  505.     
  506.     /* If the cells being moved are contiguous, check to see if there is no
  507.        change. If not, just return. */
  508.        
  509.     if (contiguous && firstRow <= destRow && destRow <= firstRow + count) {
  510.         *changed = false;
  511.         SetPort(port);
  512.         return;
  513.     }
  514.     
  515.     /* Move the selected cells to the new location one at a time. */
  516.     
  517.     LSetDrawingMode(false, theList);
  518.     SetPt(&oldCell, 0, 0);
  519.     newCell.h = 0;
  520.     index = 0;
  521.     while (LGetSelect(true, &oldCell, theList)) {
  522.         dataLen = 256;
  523.         LGetCell(cellData, &dataLen, oldCell, theList);
  524.         LDelRow(1, oldCell.v, theList);
  525.         if (oldCell.v < destRow) destRow--; 
  526.         newCell.v = destRow + index;
  527.         LAddRow(1, newCell.v, theList);
  528.         LSetCell(cellData, dataLen, newCell, theList);
  529.         index++;
  530.     }
  531.     
  532.     /* Select the new cells. */
  533.     
  534.     SetPt(&newCell, 0, destRow);
  535.     while (count--) {
  536.         MyLSetSelect(true, newCell, theList);
  537.         newCell.v++;
  538.     }
  539.     
  540.     /* Redraw the list. */
  541.  
  542.     MyLAutoScroll(theList);
  543.     rView = (**theList).rView;
  544.     InvalRect(&rView);
  545.     LSetDrawingMode(true, theList);
  546.     *changed = true;
  547.     
  548.     SetPort(port);
  549. }
  550.  
  551.  
  552.  
  553. /*----------------------------------------------------------------------------
  554.     MyLSetSelect
  555.     
  556.     Select or deselect a cell.
  557.     
  558.     Entry:    setIt = true to select, false to deselect.
  559.             theCell = the cell.
  560.             theList = handle to list record
  561. ----------------------------------------------------------------------------*/
  562.  
  563. void MyLSetSelect (Boolean setIt, Cell theCell, ListHandle theList)
  564. {
  565.     GrafPtr port;
  566.     short *cellArray;
  567.     Boolean oldSel;
  568.     
  569.     GetPort(&port);
  570.     SetPort((**theList).port);
  571.     cellArray = (**theList).cellArray;
  572.     oldSel = cellArray[theCell.v] < 0;
  573.     LSetSelect(setIt, theCell, theList);
  574.     if (!(**theList).lActive && oldSel != setIt) LDraw(theCell, theList);
  575.     SetPort(port);
  576. }
  577.  
  578.  
  579.  
  580. /*----------------------------------------------------------------------------
  581.     MyLClick
  582.     
  583.     Process a mouse-down in a list.
  584.     
  585.     Entry:    localPt = click location in local coords.
  586.             modifiers = keyboard modifiers from event record.
  587.             theList = handle to list record.
  588.             
  589.     Exit:    function result = true if double-click.
  590.     
  591.     This function is more than a bit goofy. It is used when you permit
  592.     clicks on list items in inactive windows. It sets the list's "lActive"
  593.     flag to true to fool the List Manager's LClick function into doing
  594.     cell selection and deselection. Your LDEF must be prepared to do 
  595.     proper hiliting of cells in inactive windows.
  596. ----------------------------------------------------------------------------*/
  597.  
  598. Boolean MyLClick (Point localPt, short modifiers, ListHandle theList)
  599. {
  600.     Boolean lActive, result;
  601.  
  602.     lActive = (**theList).lActive;
  603.     (**theList).lActive = true;
  604.     result = LClick(localPt, modifiers, theList);
  605.     (**theList).lActive = lActive;
  606.     return result;
  607. }
  608.  
  609.  
  610.  
  611. /*----------------------------------------------------------------------------
  612.     MyLActivate
  613.     
  614.     Activate or deactivate a list.
  615.     
  616.     Entry:    act = true to activate list, false to deactivate.
  617.             theList = handle to list record.
  618. ----------------------------------------------------------------------------*/
  619.  
  620. void MyLActivate (Boolean act, ListHandle theList)
  621. {
  622.     ControlHandle vScroll, hScroll;
  623.     Rect visible, vScrollRect, hScrollRect;
  624.     Cell theCell;
  625.  
  626.     if ((**theList).lActive == act) return;
  627.     (**theList).lActive = act;
  628.     
  629.     vScroll = (**theList).vScroll;
  630.     hScroll = (**theList).hScroll;
  631.     if (vScroll != nil) vScrollRect = (**vScroll).contrlRect;
  632.     if (hScroll != nil) hScrollRect = (**hScroll).contrlRect;
  633.     if (act) {
  634.         if (vScroll != nil) {
  635.             (**vScroll).contrlVis = 255;
  636.             InvalRect(&vScrollRect);
  637.         }
  638.         if (hScroll != nil) {
  639.             (**hScroll).contrlVis = 255;
  640.             InvalRect(&hScrollRect);
  641.         }
  642.     } else {
  643.         if (vScroll != nil) {
  644.             (**vScroll).contrlVis = 0;
  645.             InvalRect(&vScrollRect);
  646.         }
  647.         if (hScroll != nil) {
  648.             (**hScroll).contrlVis = 0;
  649.             InvalRect(&hScrollRect);
  650.         }
  651.     }
  652.     
  653.     visible = (**theList).visible;
  654.     theCell.h = 0;
  655.     for (theCell.v = visible.top; theCell.v < visible.bottom; theCell.v++)
  656.         if ((**theList).cellArray[theCell.v] < 0) InvalCell(theCell, theList);
  657. }
  658.  
  659.  
  660.  
  661. /*----------------------------------------------------------------------------
  662.     MyLNew
  663.     
  664.     Create a list.
  665.     
  666.     Entry:    rView = pointer to view rectangle.
  667.             dataBounds = pointer to data bounds rectangle.
  668.             cSize = cell size.
  669.             theProc = resource id of LDEF.
  670.             theWindow = pointer to window containing the list.
  671.             drawIt = true to draw, false to delay drawing.
  672.             hasGrow = true if has size box.
  673.             scrollHoriz = true if has horizontal scroll bar.
  674.             scrollVert = true if has vertical scroll bar.
  675.             
  676.     Exit:    function result = handle to list record.
  677. ----------------------------------------------------------------------------*/
  678.  
  679. ListHandle MyLNew (Rect *rView, Rect *dataBounds, Point cSize, short theProc,
  680.     WindowPtr theWindow, Boolean drawIt, Boolean hasGrow, 
  681.     Boolean scrollHoriz, Boolean scrollVert)
  682. {
  683.     ListHandle theList;
  684.     ControlHandle hScroll, vScroll;
  685.     
  686.     theList = LNew(rView, dataBounds, cSize, theProc, theWindow, drawIt, hasGrow,
  687.         scrollHoriz, scrollVert);
  688.     if (theWindow != FrontWindow()) {
  689.         (**theList).lActive = false;
  690.         if (scrollHoriz) {
  691.             hScroll = (**theList).hScroll;
  692.             (**hScroll).contrlVis = 0;
  693.         }
  694.         if (scrollVert) {
  695.             vScroll = (**theList).vScroll;
  696.             (**vScroll).contrlVis = 0;
  697.         }
  698.     }
  699.     return theList;
  700. }
  701.  
  702.  
  703.  
  704. /*----------------------------------------------------------------------------
  705.     PtInListCell
  706.     
  707.     Determine whether or not a point is in a list cell.
  708.     
  709.     Entry:    where = point in local coords.
  710.             theList = handle to list record.
  711.             
  712.     Exit:    function result = true if point is in a list cell.
  713. ----------------------------------------------------------------------------*/
  714.  
  715. Boolean PtInListCell (Point where, ListHandle theList)
  716. {
  717.     Rect rView, visible;
  718.     short numCells, cellHeight;
  719.  
  720.     rView = (**theList).rView;
  721.     if (!PtInRect(where, &rView)) return false;
  722.     visible = (**theList).visible;
  723.     numCells = (**theList).dataBounds.bottom;
  724.     if (visible.bottom < numCells) return true;
  725.     cellHeight = (**theList).cellSize.v;
  726.     return where.v - rView.top <= cellHeight * (numCells - visible.top);
  727. }
  728.  
  729.  
  730.  
  731. /*----------------------------------------------------------------------------
  732.     GetFirstSelectedCell 
  733.     
  734.     Get the first selected cell in a list.
  735.             
  736.     Entry:    theList = list handle.
  737.     
  738.     Exit:    function result = row number of first selected cell, or
  739.                 -1 if no selected cells.
  740. ----------------------------------------------------------------------------*/
  741.  
  742. short GetFirstSelectedCell (ListHandle theList)
  743. {
  744.     Cell theCell;
  745.  
  746.     SetPt(&theCell, 0, 0);
  747.     if (LGetSelect(true, &theCell, theList)) {
  748.         return theCell.v;
  749.     } else {
  750.         return -1;
  751.     }
  752. }
  753.  
  754.  
  755.  
  756. /*----------------------------------------------------------------------------
  757.     GetLastSelectedCell 
  758.     
  759.     Get the last selected cell in a list.
  760.             
  761.     Entry:    theList = list handle.
  762.     
  763.     Exit:    function result = row number of last selected cell, or
  764.                 -1 if no selected cells.
  765. ----------------------------------------------------------------------------*/
  766.  
  767. short GetLastSelectedCell (ListHandle theList)
  768. {
  769.     short *cellArray, *p;
  770.  
  771.     cellArray = (**theList).cellArray;
  772.     p = cellArray + (**theList).dataBounds.bottom - 1;
  773.     while (p >= cellArray) {
  774.         if (*p < 0) return p - cellArray;
  775.         p--;
  776.     }
  777.     return -1;
  778. }
  779.  
  780.  
  781.  
  782. /*----------------------------------------------------------------------------
  783.     CellSelected 
  784.     
  785.     Determine whether a cell is selected.
  786.             
  787.     Entry:    theCell = cell.
  788.             theList = list handle.
  789.     
  790.     Exit:    function result = true if cell selected.
  791. ----------------------------------------------------------------------------*/
  792.  
  793. static Boolean CellSelected (Cell theCell, ListHandle theList)
  794. {
  795.     return *((**theList).cellArray + theCell.v) < 0;
  796. }
  797.  
  798.  
  799.  
  800. /*----------------------------------------------------------------------------
  801.     ListArrowKey
  802.     
  803.     Handle an up or down arrow key for a list window.
  804.     
  805.     Entry:    theChar = upArrow or downArrow.
  806.             modifiers = modifiers field from event record.
  807.             theList = handle to list record.
  808.             prevEvent = pointer to previous event.
  809.             
  810.     Exit:    function result = error code.
  811.             *scrollIntoView = cell which should be scrolled into view.
  812. ----------------------------------------------------------------------------*/
  813.  
  814. void ListArrowKey (char theChar, short modifiers, ListHandle theList,
  815.     EventRecord *prevEvent, Cell *scrollIntoView)
  816. {
  817.     Boolean shift, command, option;
  818.     char prevChar;
  819.     short prevModifiers;
  820.     Boolean prevWasArrowKey, shiftArrowSequence;
  821.     short firstSelected, lastSelected;
  822.     short numCells;
  823.     Cell theCell = {0, 0};
  824.     Rect visible;
  825.     short pageHeight;
  826.     static short anchor;
  827.     static short prevFloatingEnd;
  828.     
  829.     numCells = (**theList).dataBounds.bottom;
  830.     if (numCells == 0) goto exit;
  831.     firstSelected = GetFirstSelectedCell(theList);
  832.     lastSelected = GetLastSelectedCell(theList);
  833.     shift = (modifiers & shiftKey) != 0;
  834.     command = (modifiers & cmdKey) != 0;
  835.     option = (modifiers & optionKey) != 0;
  836.     prevChar = prevEvent->message & 0xff;
  837.     prevModifiers = prevEvent->modifiers;
  838.     prevWasArrowKey = (prevEvent->what == keyDown || prevEvent->what == autoKey) &&
  839.         (prevChar == upArrow || prevChar == downArrow);
  840.     shiftArrowSequence = shift && prevWasArrowKey && 
  841.         (prevModifiers & shiftKey) != 0; 
  842.         
  843.     if (shift && !shiftArrowSequence) 
  844.         anchor = theChar == upArrow ? firstSelected : lastSelected;
  845.         
  846.     if (shiftArrowSequence) {
  847.         theCell.v = prevFloatingEnd;
  848.     } else {
  849.         theCell.v = theChar == upArrow ? firstSelected : lastSelected;
  850.     }
  851.     
  852.     if (!command) {
  853.         
  854.         if (theCell.v < 0) {
  855.             theCell.v = 1;
  856.         } else if (theChar == upArrow) {
  857.             theCell.v--;
  858.             if (shiftArrowSequence && theCell.v < anchor)
  859.                 while (theCell.v > 0 && CellSelected(theCell, theList))
  860.                     theCell.v--;
  861.         } else {
  862.             theCell.v++;
  863.             if (shiftArrowSequence && theCell.v > anchor)
  864.                 while (theCell.v < numCells - 1 && CellSelected(theCell, theList)) 
  865.                     theCell.v++;
  866.         }
  867.     
  868.     } else if (!option && command) {
  869.     
  870.         visible = (**theList).visible;
  871.         pageHeight = visible.bottom - visible.top - 1;
  872.         if (theChar == upArrow) {
  873.             if (theCell.v == visible.top) {
  874.                 theCell.v -= pageHeight;
  875.             } else {
  876.                 theCell.v = visible.top;
  877.             }
  878.         } else {
  879.             if (theCell.v == visible.bottom - 1) {
  880.                 theCell.v += pageHeight;
  881.             } else {
  882.                 theCell.v = visible.bottom - 1;
  883.             }
  884.         }
  885.     
  886.     } else if (option && command) {
  887.     
  888.         theCell.v = theChar == upArrow ? 0 : numCells-1;
  889.     
  890.     }
  891.     
  892.     if (theCell.v < 0) {
  893.         theCell.v = 0;
  894.     } else if (theCell.v >= numCells) {
  895.         theCell.v = numCells - 1;
  896.     }
  897.     
  898.     if (!shift || anchor < 0) {
  899.         SelectSingleListItem(theList, theCell);
  900.     } else if (!shiftArrowSequence) {
  901.         if (anchor <= theCell.v) {
  902.             SelectCellRange(true, anchor, theCell.v, theList);
  903.         } else {
  904.             SelectCellRange(true, theCell.v, anchor, theList);
  905.         }
  906.     } else {
  907.         if (anchor <= prevFloatingEnd && prevFloatingEnd < theCell.v) {
  908.             SelectCellRange(true, prevFloatingEnd + 1, theCell.v, theList);
  909.         } else if (anchor <= theCell.v && theCell.v < prevFloatingEnd) {
  910.             SelectCellRange(false, theCell.v + 1, prevFloatingEnd, theList);
  911.         } else if (theCell.v < prevFloatingEnd && prevFloatingEnd <= anchor) {
  912.             SelectCellRange(true, theCell.v, prevFloatingEnd - 1, theList);
  913.         } else if (prevFloatingEnd < theCell.v && theCell.v <= anchor) {
  914.             SelectCellRange(false, prevFloatingEnd, theCell.v - 1, theList);
  915.         } else if (prevFloatingEnd <= anchor && anchor <= theCell.v) {
  916.             SelectCellRange(false, prevFloatingEnd, anchor - 1, theList);
  917.             SelectCellRange(true, anchor, theCell.v, theList);
  918.         } else if (theCell.v <= anchor && anchor <= prevFloatingEnd) {
  919.             SelectCellRange(true, theCell.v, anchor, theList);
  920.             SelectCellRange(false, anchor + 1, prevFloatingEnd, theList);
  921.         } 
  922.     }
  923.     prevFloatingEnd = theCell.v;
  924.     
  925. exit:
  926.  
  927.     *scrollIntoView = theCell;
  928. }
  929.  
  930.  
  931.  
  932. /*----------------------------------------------------------------------------
  933.     SetListClickLoop
  934.     
  935.     Set a click loop function for a list.
  936.     
  937.     Entry:    theList = handle to list record.
  938.             clickLoopUPP = click loop UPP.
  939.             
  940.     This function contains a hack by Dave Radcliffe of Apple DTS to 
  941.     solve a problem with native mode List Manager click loop functions.
  942.     The problem is that the 68K List Manager expects the Boolean function
  943.     result in the Z bit in the condition code register, but the Mixed
  944.     Mode Manager doesn't set this bit correctly. The hack uses a 68K 
  945.     wrapper in native mode to set the Z bit.
  946.     
  947.     This function should be called to set a click loop function
  948.     immediately before calling LClick or MyLClick.
  949. ----------------------------------------------------------------------------*/
  950.  
  951. void SetListClickLoop (ListHandle theList, ListClickLoopUPP clickLoopUPP)
  952. {
  953. #ifdef powerc
  954.  
  955.     #pragma options align=mac68k
  956.     static struct clickLoopGlue {
  957.         long move;                            /* movea.l clickLoopUPP,a0 */
  958.         short jsr;                            /* jsr (a0) */
  959.         short tst;                            /* tst.b d0 */
  960.         short rts;                            /* rts */
  961.         ListClickLoopUPP clickLoopUPP;        /* the UPP */
  962.     } clickLoop68K = {
  963.         0x207A0008,
  964.         0x4E90,
  965.         0x4A00,
  966.         0x4E75,
  967.         0
  968.     };
  969.     #pragma options align=reset
  970.     
  971.     clickLoop68K.clickLoopUPP = clickLoopUPP;
  972.     (**theList).lClickLoop = (ListClickLoopUPP)&clickLoop68K;
  973.     
  974. #else
  975.  
  976.     (**theList).lClickLoop = clickLoopUPP;
  977.     
  978. #endif
  979. }